﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Rainbow.ObjectFlow.Framework;

namespace TestDoc
{
    internal class CreateReportStructure : BasicOperation<Program.TestReport>
    {
        public override Program.TestReport Execute(Program.TestReport data)
        {
            TestParser parser = CreateTestParser(data.Properties);
            if (parser == null)
            {
                //REVIEW
                //DisplayError(thisExe, ErrorFileNotFound);
                SetSuccessResult(false);
            }

            data.Tests = RunReport(parser);

            return data;
        }

        private static TestParser CreateTestParser(Program.CommandLineArguments commandLine)
        {
            string assemblyName = commandLine.File;
            TestParser parser;

            try
            {
                AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve +=
                    new ResolveEventHandler(CurrentDomain_ReflectionOnlyAssemblyResolve);

                var target = Assembly.ReflectionOnlyLoadFrom(assemblyName);

                parser = new TestParser(target);
            }
            catch (Exception e)
            {
                parser = null;

            }
            return parser;
        }

        private static IList<MethodInfoSmall> RunReport(TestParser parser)
        {
            var assemblyMethods = parser.GetTests();
            return parser.FormatTestReport(assemblyMethods);
        }

        static Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
        {
            Assembly assembly = null;
            var dependancy = new AssemblyName(args.Name);

            assembly = TryAssemblyLoadFrom(dependancy, Directory.GetCurrentDirectory());

            if (AssemblyLoaded(assembly))
            {
                try
                {
                    // load from gac
                    assembly = Assembly.ReflectionOnlyLoad(args.Name);
                }
                catch (Exception)
                {
                    IEnumerable<string> loadFrom = FindInProgramFiles(dependancy);

                    assembly = TryAssemblyLoadFrom(dependancy, loadFrom);
                    if (assembly == null)
                    {
                        loadFrom = SearchAllVolumes(dependancy);
                        assembly = TryAssemblyLoadFrom(dependancy, loadFrom);
                    }
                }
            }

            return assembly;
        }

        private static bool AssemblyLoaded(Assembly assembly)
        {
            return assembly == null;
        }

        private static Assembly TryAssemblyLoadFrom(AssemblyName dependancy, string path)
        {
            return TryAssemblyLoadFrom(dependancy, new List<string> { path });
        }

        private static Assembly TryAssemblyLoadFrom(AssemblyName dependancy, IEnumerable<string> path)
        {
            Assembly assembly = null;
            if (path != null)
            {
                foreach (string s in path)
                {
                    string loadfrom = s + Path.DirectorySeparatorChar + dependancy.Name + ".dll";
                    try
                    {
                        if (File.Exists(loadfrom))
                        {
                            assembly = Assembly.ReflectionOnlyLoadFrom(loadfrom);
                            break;
                        }
                    }
                    catch (Exception)
                    {
                    }
                }
            }

            return assembly;
        }

        private static ICollection<string> SearchAllVolumes(AssemblyName assembly)
        {
            ICollection<string> candidates = new List<string>();
            DriveInfo[] d = DriveInfo.GetDrives();
            foreach (DriveInfo info in d)
            {
                try
                {
                    if (info.DriveType == DriveType.Fixed)
                    {
                        IEnumerable<string> found = FindFile(assembly.Name + ".dll", info.RootDirectory.Name);
                        ((List<string>)candidates).AddRange(found);
                    }
                }
                catch (Exception)
                {

                }
            }

            return candidates;
        }

        private static IEnumerable<string> FindInProgramFiles(AssemblyName dependancy)
        {
            string pf = Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFiles);

            return FindFile(dependancy.Name + ".dll", pf);
        }

        private static IEnumerable<string> FindFile(string file, string directory)
        {
            foreach (var d in Directory.GetDirectories(directory))
            {
                foreach (var fileImage in Directory.GetFiles(d, file))
                {
                    yield return d;
                }
            }
        }
    }
}
